package com.moon.game.command;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jboss.netty.channel.MessageEvent;

import com.moon.game.constants.Constants;
import com.moon.game.constants.ErrorCode;
import com.moon.game.exception.GException;
import com.moon.game.manager.PlayerMG;
import com.moon.game.message.ObjMessage;
import com.moon.game.server.SessionPool;
import com.moon.game.service.MainLandService;
import com.moon.game.service.PlayerService;
import com.moon.game.util.GDateUtils;
import com.moon.game.vos.ErrorMsg;
import com.moon.game.vos.PlayerVo;

public abstract class BaseCmd implements Command {
	private static final Logger log = Logger.getLogger(BaseCmd.class);

	@Override
	public void execute(RequestSession request) {
		try {
			runOnce(request);
		} catch (GException e) {
			log.error(e.getMessage(), e);
			doResponseErrorMsg(request, ErrorMsg.build(ErrorCode.ERR_SYS));
		}
	}

	@Override
	public void executeTimeout(RequestSession request) {

	}

	/**
	 * 返回系统错误提示信息
	 * 
	 * @param req
	 * @param msg
	 * @throws PpseaException
	 */
	protected void doResponseErrorMsg(RequestSession req, ErrorMsg msg) {
		ObjMessage resp = req.getClientResponse();
		resp.setStatus(Constants.STATUS_FAIL);
//		resp.setCmd(req.getCmd());
		resp.setCmd("E_INFO");
		resp.setVO(PAR_ID, req.getPlayerId());
		resp.setVO(TAG_ERR_MSG, msg);
		try {
			send2Client(req, resp);
		} catch (GException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 返回系统错误提示信息
	 * 
	 * @param req
	 * @param msg
	 * @throws PpseaException
	 */
	protected void doResponsePsError(RequestSession req) {
		ObjMessage resp = req.getClientResponse();
		resp.setStatus(Constants.STATUS_FAIL);
		resp.setCmd("E_PS");
		resp.setVO(PAR_ID, req.getPlayerId());
		resp.setVO(TAG_ERR_MSG, ErrorMsg.build(ErrorCode.ERR_PAR, "错误参数请求！"));
		try {
			send2Client(req, resp);
		} catch (GException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 设置命令的返回状态
	 **/
	protected void doResult(RequestSession req, String status) throws GException {
		ObjMessage result = req.getClientResponse();
		result.setStatus(status);
		response(req, result);
	}

	/**
	 * 发送到客户端
	 * 
	 * @param reqSession
	 * @param resp
	 */
	private void send2Client(RequestSession reqSession, ObjMessage resp)
			throws GException {
		try {
			// NettyUtil.out(reqSession, resp);
			if ("LOG_IN".equals(resp.getCmd())) {
				resp.setCmd("P_VM");
			}
			MessageEvent event = reqSession.getEvent();
			event.getChannel().write(resp, event.getRemoteAddress());
		} catch (Exception e) {
			 throw new GException(e);
		} finally {
			// 置状态为结束,这样timeout_q超时队列检测到时有依据可忽略
			reqSession.setStatus(-1);
			// 流程结束,删除数据
			SessionPool.inst.delReqSession(reqSession.getSessionId());
			PlayerService.logFlow(reqSession);
			//解锁人物
			if (reqSession.getPlayerId() != null ) {
				PlayerMG.instance.unLock(reqSession.getPlayerId());
			}
		}
	}

	/**
	 * 返回到Proxy
	 * 
	 * @param req
	 * @param resp
	 * @throws PpseaException
	 * @throws Exception
	 */
	public void response(RequestSession req, ObjMessage resp) throws GException {
		send2Client(req, resp);
	}
	
	/**
	 * 获取玩家(在线直接返回,不在线从Profile响应中取回)
	 * 
	 * @param req
	 * @return
	 * @throws PpseaException
	 */
	protected PlayerVo getPlayerVo(RequestSession req) throws GException {
		PlayerVo PlayerVo = req.getPlayer();
		if (PlayerVo == null) {
			PlayerVo = PlayerService.doGetPlayer(req.getSid(), req.getPlayerId());
		}
		return PlayerVo;
	}

	/**
	 * 初始化人物app对象
	 * 
	 * @param req
	 * @throws GException
	 */
	protected void doInit(RequestSession req) throws GException {
		String sid = req.getClientRequest().getSid();
		if (sid == null)
			throw new GException("sid is null");
		String playerId = req.getClientRequest().getPid();
		if (playerId == null && !"P_CP".equals(req.getCmd()) && !"LOG_IN".equals(req.getCmd()))
			throw new GException("player id is null");
		req.setTempData(Command.PAR_ID, playerId);
		req.setTempData(Command.PAR_SID, sid);
		// 忽略请求，直接skip到第二个状态（检查本地是否有cache）
		PlayerVo PlayerVo = req.getPlayer();
		req.setTempData(PAR_ONLINE, Constants.STATUS_OFFLINE);
		if (PlayerVo != null) {
			req.setTempData(PAR_ONLINE, Constants.STATUS_ONLINE);
		}
		skipStatus(req, req.getStatusPlus());
	}

	/**
	 * 账号安全等检查
	 * 
	 * @param req
	 * @throws pException
	 */
	protected void doCheckSelf(RequestSession req) throws GException {
		boolean isCheckSign = req.getClientRequest().isCheckSign();
		String sid = (String) req.getTempData(Command.PAR_SID);
		String playerId = (String) req.getTempData(Command.PAR_ID);
		PlayerVo PlayerVo = req.getPlayer();
		int banAccountTime = 0;
		// 已经在内存中，并且可以忽略发请求给Profile
		if (PlayerVo != null) {
			banAccountTime = PlayerVo.getBanAccountTime();
		} else {
			//load player from DB
			PlayerVo = PlayerService.doGetPlayer(sid, playerId);
			if (PlayerVo == null) {
				// 获取玩家资料失败
				req.setVO("E_INFO", ErrorMsg.build(ErrorCode.ERR_NO_PLAYER, "获取人物信息失败！"));
				throw new GException("账号[" + playerId + "]不存在!");
			}
		}

		// 封号判断
		if (banAccountTime > 0 && GDateUtils.now() - banAccountTime <= 0) {
			SimpleDateFormat formatter = new SimpleDateFormat(
					"yyyy-MM-dd HH:mm:ss");
			String banAccountDate = formatter.format(new Date(
					banAccountTime * 1000l));
			doResponseErrorMsg(req,ErrorMsg.build(ErrorCode.ERR_KICK, "你已被封号,解封时间:"
							+ banAccountDate));
			return;
		}

		// 书签校验
		if (isCheckSign) {
			// TODO:检查标签
			// verifySign(req, passwd, Constants.MD5_KEY);
		}

		// 更新访问时间
		PlayerMG.instance.access(PlayerVo);
		PlayerVo = PlayerMG.instance.getPlayer(playerId);
		req.setTempData(Command.PAR_PLAYER_VO, PlayerVo);

		// 必须返回的全局变量
		req.setVO(PAR_ID, playerId);
		req.setVO(PAR_SID, PlayerVo.getSid());
		req.setVO(PAR_SAFE_CODE, String.valueOf(PlayerVo.getSafecode()));
		req.setVO(PAR_LOCATION_NAME, MainLandService.getMainLand(PlayerVo.getLocation()).getName());
		req.setVO(PAR_LOCATION, PlayerVo.getLocation());

		// 设置统计数据
		req.setTempData(Constants.FLOW_EXP, PlayerVo.getExp());
		req.setTempData(Constants.FLOW_MONEY, PlayerVo.getMoney());
		req.setTempData(Constants.FLOW_HP, PlayerVo.getHp());
		req.setTempData(Constants.FLOW_MP, PlayerVo.getMp());
		req.setTempData(Constants.FLOW_ROOM, PlayerVo.getMaxRoom());
		req.setTempData(Constants.FLOW_GOLD, PlayerVo.getGold());
		req.setTempData(Constants.FLOW_BURDEN, PlayerVo.getBurden());
		skipStatus(req, req.getStatusPlus());
	}

	/**
	 * 跳转到指定状态
	 * 
	 * @param data
	 * @return
	 * @throws PpseaException
	 */
	public static void skipStatus(RequestSession req, int status) {
		req.setStatus(status);
		SessionPool.UniData uniData = new SessionPool.UniData(SessionPool.UniData.FROM_NET, req.getSessionId(), null);
		SessionPool.inst.addData(uniData);
	}
	
	/**
	 * 发起另外请求
	 * 
	 * @param cmd
	 * @param req
	 * @param dataMap
	 * @param ps
	 */
	public static void callOtherCmd(String cmd, RequestSession req, Map<String, Object> dataMap, String... ps) {
		// 结束之前的命令日志,开始下一个命令的日志
		PlayerService.logFlow(req);
		try {
			ObjMessage request = new ObjMessage();
			request.setCmd(cmd);
			request.setSid(req.getSid());
			request.setPid(req.getPlayerId());
			request.setPs(ps);
			request.setCheckSign(false);
			request.setStartTime(req.getClientRequest().getStartTime());
			if (dataMap != null && !dataMap.isEmpty()) {
				for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
					request.setVO(entry.getKey(), entry.getValue());
				}
				log.debug("Data:{" + dataMap + "}");
			}
			SessionPool.UniData uniData = new SessionPool.UniData(SessionPool.UniData.FROM_CLIENT, request, req.getEvent());
			SessionPool.inst.addData(uniData);
		} catch (Exception e) {
			log.error("call other excepiton:", e);
		} finally {// 清除原命令
			SessionPool.inst.delReqSession(req.getSessionId());
		}
	}

	protected abstract void runOnce(RequestSession req) throws GException;

	protected abstract void doResponse(RequestSession req) throws GException;
}
